package com.compose.universally.okhttp

import android.util.Log
import com.blankj.utilcode.util.FileUtils
import com.blankj.utilcode.util.GsonUtils
import okhttp3.*
import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.RequestBody.Companion.asRequestBody
import okhttp3.RequestBody.Companion.toRequestBody
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import java.io.InputStream
import java.net.URLConnection
import java.util.concurrent.TimeUnit
import kotlin.collections.ArrayList
import kotlin.collections.HashMap

class OKHttp {
    companion object {
        private var okHttp = OKHttp()


        /**
         * 获取client
         */
        private fun getHttpClient() {
            if (okHttp.client == null) {
                okHttp.client = OkHttpClient.Builder().addInterceptor(OKHttpLogInterceptor())
                    .connectTimeout(30, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS)
                    .callTimeout(30, TimeUnit.SECONDS).build()
            }
        }

        /**
         * post请求
         * @param url String? 地址
         * @return DLHttp
         */
        fun post(url: String?): OKHttp {
            getHttpClient()
            okHttp.requestMethod = okHttp.requestPost
            okHttp.requestUrl = url
            return okHttp
        }

        /**
         * put请求
         * @param url String? 地址
         * @return DLHttp
         */
        fun put(url: String?): OKHttp {
            getHttpClient()
            okHttp.requestMethod = okHttp.requestPut
            okHttp.requestUrl = url
            return okHttp
        }

        /**
         * get请求
         * @param url String?
         * @return DLHttp
         */
        fun get(url: String?): OKHttp {
            getHttpClient()
            okHttp.requestMethod = okHttp.requestGet
            okHttp.requestUrl = url
            return okHttp
        }

        /**
         * 下载文件
         * @param realURL String?
         * @param destFileDir String?
         *
         * @return DLHttp
         */
        fun downloadFile(realURL: String?, destFileDir: String?, downFileName: String): OKHttp {
            getHttpClient()
            okHttp.downUrl = realURL
            okHttp.fileDir = destFileDir
            okHttp.downFileName = downFileName
            return okHttp
        }
    }

    // post请求
    private val requestPost = 1001

    // get请求
    private val requestGet = 1002

    // put请求
    private val requestPut = 1003

    // 默认请求方式GET
    private var requestMethod = requestGet

    // JSON格式提交
    private val jsonType = 301

    // 提交文件
    private val fileType = 302

    // 表单提交
    private val formType = 303

    // 默认表单提交
    private var buildType = formType


    private var client: OkHttpClient? = null
    private var requestUrl: String? = null

    private var downUrl: String? = null
    private var fileDir: String? = null
    private var downFileName = ""


    private val mediaType: MediaType =
        "application/json; charset=utf-8".toMediaTypeOrNull()!! // JSON的mediaType表头
    private var requestBody: RequestBody? = null
    private var request: Request? = null

    // 请求header
    private var headers = Headers.Builder()

    // json实体类
    private var jsonObject: Any? = null

    // 参数
    private var params: MutableMap<String, Any?> = HashMap()

    // 文件参数
    private val files: MutableList<File> = ArrayList()

    fun addHeader(map: Map<String, Any?>): OKHttp {
        for (entry in map) {
            headers.add(entry.key, entry.value.toString())
        }
        return this
    }

    fun addHeader(key: String, value: Any): OKHttp {
        headers.add(key, value.toString())
        return this
    }

    /**
     * 添加单一参数
     * @param key String
     * @param value Any
     * @return DLHttp
     */
    fun add(key: String, value: Any): OKHttp {
        params[key] = value.toString()
        return this
    }

    /**
     * 添加参数集合
     * @param map Map<String, Any?>
     * @return DLHttp
     */
    fun add(map: Map<String, Any?>): OKHttp {
        params.putAll(map)
        return this
    }

    /**
     * 添加参数类
     * @param any Any
     * @return DLHttp
     */
    fun add(any: Any): OKHttp {
        this.jsonObject = any
        return this
    }


    /**
     * 添加文件参数
     * @param file File
     * @return DLHttp
     */
    fun add(file: File): OKHttp {
        files.add(file)
        return this
    }

    /**
     * 以JSON的方式提交数据
     * @param callBack IDLHttpCallBack<T>
     */
    fun <T> buildByJson(callBack: OKHttpCallBack) {
        buildType = jsonType
        build(callBack)
    }

    /**
     * 基于http的文件上传（传入文件数组和key）混合参数和文件请求
     * 通过addFormDataPart可以添加多个上传的文件
     */
    fun <T> buildByFile(callBack: OKHttpCallBack) {
        val multipartBody: MultipartBody.Builder = MultipartBody.Builder()
        multipartBody.setType(MultipartBody.FORM)
        for (key in params.keys) {
            multipartBody.addFormDataPart(key, params[key].toString())
        }
        if (jsonObject != null) {
            multipartBody.addPart(GsonUtils.toJson(jsonObject).toRequestBody(mediaType))
        }
        for ((index, file) in files.withIndex()) {
            val fileName = file.name
            val fileBody = file.asRequestBody(guessMimeType(fileName).toMediaTypeOrNull())
            multipartBody.addFormDataPart("file$index", fileName, fileBody)
        }
        requestBody = multipartBody.build()
        buildType = fileType
        build(callBack)
    }

    /**
     * 获取文件提交的guessMimeType
     * @param fileName String
     * @return String
     */
    private fun guessMimeType(fileName: String): String {
        val fileNameMap = URLConnection.getFileNameMap()
        var contentTypeFor = fileNameMap.getContentTypeFor(fileName)
        if (contentTypeFor == null) {
            contentTypeFor = "application/octet-stream"
        }
        return contentTypeFor
    }


    fun build(callBack: OKHttpCallBack) {
        // 使用默认header
        for (entry in OKHttpBaseCallBack.headerMap) {
            headers.add(entry.key, entry.value.toString())
        }
        // 开始请求
        callBack.Builder().start()
        // 数据上传方式
        when (buildType) {
            jsonType -> {
                if (jsonObject != null) {
                    requestBody = GsonUtils.toJson(jsonObject).toRequestBody(mediaType)
                } else if (params.isNotEmpty()) {
                    requestBody = GsonUtils.toJson(params).toRequestBody(mediaType)
                }
            }

            fileType -> {
            }

            formType -> {
                // form表单提交
                val builder: FormBody.Builder = FormBody.Builder()
                for (key in params.keys) {
                    builder.add(key, params[key].toString())
                }
                requestBody = builder.build()
            }
        }
        // 数据上传方式
        when (requestMethod) {
            requestGet -> {
                val urlBuilder: HttpUrl.Builder = requestUrl!!.toHttpUrlOrNull()!!.newBuilder()
                for (key in params.keys) {
                    urlBuilder.addQueryParameter(key, params[key].toString())
                }
                request =
                    Request.Builder().url(urlBuilder.build()).headers(headers.build()).get().build()
            }

            requestPost -> {
                request =
                    Request.Builder().url(requestUrl!!).headers(headers.build()).post(requestBody!!)
                        .build()
            }

            requestPut -> {
                request =
                    Request.Builder().url(requestUrl!!).headers(headers.build()).put(requestBody!!)
                        .build()
            }
        }
        val call: Call = client!!.newCall(request!!)
        call.enqueue(object : Callback {
            // 请求错误
            override fun onFailure(call: Call, e: IOException) {
                callBack.Builder().error(-1, e.message)
            }

            // 请求结果
            override fun onResponse(call: Call, response: Response) {
                if (response.code == 200) {
                    try {
                        val json = response.body!!.string()
                        Log.e("OKHttp response：${response.request.url}", json)
                        callBack.Builder().onResponse(json)
                        val baseObject = JSONObject(json)
                        when (baseObject.getInt(OKHttpBaseCallBack.code)) {
                            OKHttpBaseCallBack.successCode -> {
                                if (OKHttpConfig.useDefaultStr) {
                                    if (OKHttpBaseCallBack.data.isNotBlank()) {
                                        val iOKHttpBaseCallBack = GsonUtils.fromJson(
                                            baseObject.getString(OKHttpBaseCallBack.data),
                                            OKHttpBaseCallBack::class.java
                                        )
                                        callBack.Builder().success(iOKHttpBaseCallBack!!.data)
                                    } else {
                                        val iOKHttpBaseCallBack = GsonUtils.fromJson(
                                            json, OKHttpBaseCallBack::class.java
                                        )
                                        callBack.Builder().success(iOKHttpBaseCallBack!!.data)
                                    }
                                } else {
                                    callBack.Builder()
                                        .success(GsonUtil.fromJson(json, callBack.type))
                                }
                            }

                            else -> {
                                callBack.Builder().error(
                                    baseObject.getInt(OKHttpBaseCallBack.code),
                                    baseObject.getString(OKHttpBaseCallBack.msg)
                                )
                            }
                        }
                        callBack.Builder().info(baseObject.getString(OKHttpBaseCallBack.msg))
                        response.close()
                    } catch (e: JSONException) {
                        e.printStackTrace()
                        Log.e("OKHttp response error：", "${response.request.url},${e.message}")
                        callBack.Builder().error(-2, "网络开小差,请稍后重试")
                    }
                } else {
                    callBack.Builder().error(response.code, response.message)
                }
                callBack.Builder().end()
            }
        })
    }

    /**
     * 下载文件
     * */
    private var downCall: Call? = null

    fun download(callBack: OKHttpFileCallBack) {
        val request: Request = Request.Builder().url(downUrl!!).build()
        downCall = client!!.newCall(request)
        downCall!!.enqueue(object : Callback {
            override fun onResponse(call: Call, response: Response) {
                when (response.code) {
                    200 -> {
                        var `is`: InputStream? = null
                        val buf = ByteArray(2048)
                        var len: Int
                        var fos: FileOutputStream? = null
                        // 储存下载文件的目录
                        val dir = File(fileDir!!)
                        if (!dir.exists()) {
                            dir.mkdirs()
                        }
                        FileUtils.createFileByDeleteOldFile(dir.toString() + downFileName)
                        val updateFile = FileUtils.getFileByPath("$dir/$downFileName")
                        try {
                            `is` = response.body!!.byteStream()
                            val total: Long = response.body!!.contentLength()
                            callBack.Builder().start(total)
                            fos = FileOutputStream(updateFile)
                            var sum: Long = 0
                            while (`is`.read(buf).also { len = it } != -1) {
                                fos.write(buf, 0, len)
                                sum += len.toLong()
                                callBack.Builder().progress(sum)
                            }
                            fos.flush()
                            fos.close()
                            callBack.Builder().success(updateFile)
                        } catch (e: Exception) {
                            callBack.Builder().error("下载失败${e.message}")
                        } finally {
                            try {
                                `is`?.close()
                            } catch (_: IOException) {
                            }
                            try {
                                fos?.close()
                            } catch (e: IOException) {
                                e.printStackTrace()
                            }
                        }
                    }

                    else -> {
                        callBack.Builder().error("下载失败")
                    }

                }
            }

            override fun onFailure(call: Call, e: IOException) {
                callBack.Builder().error(e.message)
            }
        })
    }

    fun getFileName(url: String?): String {
        val separatorIndex = url!!.lastIndexOf("/")
        return if (separatorIndex < 0) url else url.substring(separatorIndex + 1, url.length)
    }


    fun cancelDownload() {
        if (downCall != null) {
            downCall!!.cancel()
        }
    }

}


//external  GsonUtil